//**************************************************
// Description: generate protocol frame
//**************************************************

//**************************************************
// include files
//**************************************************
`include "protocol_define.v"

module tx_protocol_generator #(
    parameter IN_DATA_WIDTH         = 128   ,
    parameter IN_MOD_WIDTH          = 4     ,
    parameter OUT_ORDER_WIDTH       = 30    ,
    parameter OUT_ADDR_WIDTH        = 32    ,
    parameter OUT_DATA_WIDTH        = 32    ,
    parameter ACTION_ADDITION_SEQ   = 32'hA5_5A_A5_5A
    ) (
    input                                               clk ,
    input                                               rst_n ,
    // response module
    output                                              protocol_gen_rdy ,
    output                                              protocol_start_trans ,
    input       [IN_DATA_WIDTH-1-32:0]                  protocol_gen_data ,
    input                                               protocol_gen_sav ,
    input                                               protocol_gen_val ,
    input                                               protocol_gen_sop ,
    input                                               protocol_gen_eop ,
    input                                               convert_frame_info ,
    // frame info FIFO
    input       [47:0]                                  rd_DATALINK_dst_mac_fifo ,
    input       [47:0]                                  rd_DATALINK_src_mac_fifo ,
    input       [31:0]                                  rd_NETWORK_src_IP_fifo ,
    input       [31:0]                                  rd_NETWORK_dst_IP_fifo ,
    input       [15:0]                                  rd_TRANSPORT_src_port_fifo ,
    input       [15:0]                                  rd_TRANSPORT_dst_port_fifo ,

    // store frame FIFO
    output                                              protocol_FIFO_wr_en ,
    output      [1+1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:0]    protocol_FIFO_wr_data ,
    // UDP generator module
    input                                               UDP_generator_rdy ,
    output      [47:0]                                  DATALINK_dst_mac_fifo_o ,
    output      [47:0]                                  DATALINK_src_mac_fifo_o ,
    output      [31:0]                                  NETWORK_src_IP_fifo_o ,
    output      [31:0]                                  NETWORK_dst_IP_fifo_o ,
    output      [15:0]                                  TRANSPORT_src_port_fifo_o ,
    output      [15:0]                                  TRANSPORT_dst_port_fifo_o ,
    output      [10:0]                                  protocol_length ,
    output      [15:0]                                  protocol_CRC_checksum ,
    output                                              protocol_gen_finish
) ;

    //----------------------------------------------
    // internal signal declare
    //----------------------------------------------
    // frame info FIFO content
    reg                     convert_frame_info_ff1 ;
    reg                     protocol_gen_val_ff1 ; // be used for the last 16-Byte content
    // store frame info
    reg     [47:0]          DATALINK_dst_mac_fifo_r ;
    reg     [47:0]          DATALINK_src_mac_fifo_r ;
    reg     [31:0]          NETWORK_src_IP_fifo_r ;
    reg     [31:0]          NETWORK_dst_IP_fifo_r ;
    reg     [15:0]          TRANSPORT_src_port_fifo_r ;
    reg     [15:0]          TRANSPORT_dst_port_fifo_r ;

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)  
            convert_frame_info_ff1 <= 1'b0 ;
        else
            convert_frame_info_ff1 <= convert_frame_info ;
    end
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)  
            protocol_gen_val_ff1 <= 1'b0 ;
        else
            protocol_gen_val_ff1 <= protocol_gen_val ;
    end
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            DATALINK_dst_mac_fifo_r <= 48'b0 ;
            DATALINK_src_mac_fifo_r <= 48'b0 ;
            NETWORK_src_IP_fifo_r <= 32'b0 ;
            NETWORK_dst_IP_fifo_r <= 32'b0 ;
            TRANSPORT_src_port_fifo_r <= 16'b0 ;
            TRANSPORT_dst_port_fifo_r <= 16'b0 ;
        end
        else if(convert_frame_info_ff1) begin // note: exchange the "dst" and "src"
            DATALINK_dst_mac_fifo_r <= rd_DATALINK_src_mac_fifo ;
            DATALINK_src_mac_fifo_r <= rd_DATALINK_dst_mac_fifo ;
            NETWORK_src_IP_fifo_r <= rd_NETWORK_dst_IP_fifo ;
            NETWORK_dst_IP_fifo_r <= rd_NETWORK_src_IP_fifo ;
            TRANSPORT_src_port_fifo_r <= rd_TRANSPORT_dst_port_fifo ;
            TRANSPORT_dst_port_fifo_r <= rd_TRANSPORT_src_port_fifo ;
        end
        else begin
            DATALINK_dst_mac_fifo_r <= DATALINK_dst_mac_fifo_r ;
            DATALINK_src_mac_fifo_r <= DATALINK_src_mac_fifo_r ;
            NETWORK_src_IP_fifo_r <= NETWORK_src_IP_fifo_r ;
            NETWORK_dst_IP_fifo_r <= NETWORK_dst_IP_fifo_r ;
            TRANSPORT_src_port_fifo_r <= TRANSPORT_src_port_fifo_r ;
            TRANSPORT_dst_port_fifo_r <= TRANSPORT_dst_port_fifo_r ;
        end
    end

    //----------------------------------------------
    // output signal declare
    //----------------------------------------------
    // response module
    // reg                                             protocol_gen_rdy_r ;
    reg                                             protocol_start_trans_r ;
    // store frame FIFO
    reg                                             protocol_FIFO_wr_en_r ;
    reg     [1+1+IN_MOD_WIDTH+IN_DATA_WIDTH-1:0]    protocol_FIFO_wr_data_r ;
    // UDP generator
    reg     [47:0]                                  DATALINK_dst_mac_fifo_o_r ;
    reg     [47:0]                                  DATALINK_src_mac_fifo_o_r ;
    reg     [31:0]                                  NETWORK_src_IP_fifo_o_r ;
    reg     [31:0]                                  NETWORK_dst_IP_fifo_o_r ;
    reg     [15:0]                                  TRANSPORT_src_port_fifo_o_r ;
    reg     [15:0]                                  TRANSPORT_dst_port_fifo_o_r ;
    reg     [10:0]                                  protocol_length_r ;
    reg     [15:0]                                  protocol_CRC_checksum_r ;
    reg                                             protocol_gen_finish_r ;

    // temp signal(store response)
    reg     [IN_DATA_WIDTH-1-32:0]                  protocol_gen_data_ff ;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            protocol_gen_data_ff <= {(IN_DATA_WIDTH-32){1'b0}} ;
        else if(protocol_gen_val)
            protocol_gen_data_ff <= protocol_gen_data ;
        else
            protocol_gen_data_ff <= protocol_gen_data_ff ;
    end
    // temp signal(be used for CRC checksum)
    reg     [15:0]                                  protocol_CRC_checksum_header ;
    wire    protocol_CRC_checksum_header_all_zero ;
    assign  protocol_CRC_checksum_header_all_zero = (protocol_CRC_checksum_header==16'b0) ;
    // temp signal(be used to mark the previous data is valid)
    reg                                             first_gen_signal ;
    // temp signal(be used to generate sop)
    reg                                             frist_write_signal ;

//**************************************************
// FSM 
//**************************************************
    localparam PROT_GEN_IDLE            = 3'd0 ;
    localparam PROT_GEN_JUDGE           = 3'd1 ;
    localparam PROT_GEN_STORE           = 3'd2 ;
    localparam PROT_GEN_LAST            = 3'd3 ;
    localparam PROT_GEN_CRC_WAIT        = 3'd4 ;
    localparam PROT_GEN_CRC_GEN         = 3'd5 ;
    localparam PROT_GEN_END             = 3'd6 ;

    reg[2:0]    prot_gen_state_c ;
    reg[2:0]    prot_gen_state_n ;

    // FSM(1)
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            prot_gen_state_c <= PROT_GEN_IDLE ;
        else
            prot_gen_state_c <= prot_gen_state_n ;
    end
    // FSM(2)
    always @(*) begin
        case(prot_gen_state_c)
            PROT_GEN_IDLE: begin
                if( UDP_generator_rdy && protocol_gen_sav )
                    prot_gen_state_n = PROT_GEN_JUDGE ;
                else
                    prot_gen_state_n = PROT_GEN_IDLE ;
            end

            PROT_GEN_JUDGE: begin
                if( protocol_gen_eop ) // note : data can be val
                    prot_gen_state_n = PROT_GEN_LAST ;
                else if( protocol_gen_val )
                    prot_gen_state_n = PROT_GEN_STORE ;
                else
                    prot_gen_state_n = PROT_GEN_JUDGE ;
            end

            PROT_GEN_STORE: begin
                prot_gen_state_n = PROT_GEN_JUDGE ;
            end

            PROT_GEN_LAST: begin
                prot_gen_state_n = PROT_GEN_CRC_WAIT ;
            end

            PROT_GEN_CRC_WAIT: begin
                prot_gen_state_n = PROT_GEN_CRC_GEN ;
            end

            PROT_GEN_CRC_GEN: begin
                if( protocol_CRC_checksum_header_all_zero )
                    prot_gen_state_n = PROT_GEN_END ;
                else
                    prot_gen_state_n = PROT_GEN_CRC_GEN ;
            end

            PROT_GEN_END: begin
                prot_gen_state_n = PROT_GEN_IDLE ;
            end

            default:
                prot_gen_state_n = PROT_GEN_IDLE ;
        endcase
    end
    // FSM(3)
    // start trans
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            protocol_start_trans_r <= 1'b0 ;
        else if(prot_gen_state_c == PROT_GEN_JUDGE)
            protocol_start_trans_r <= 1'b1 ;
        else
            protocol_start_trans_r <= 1'b0 ;
    end
    // fist generate signal
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            first_gen_signal <= 1'b1 ;
        else if ( prot_gen_state_c==PROT_GEN_IDLE )
            first_gen_signal <= 1'b1 ;
        else if ( prot_gen_state_c==PROT_GEN_STORE )
            first_gen_signal <= 1'b0 ;
        else
            first_gen_signal <= first_gen_signal ;
    end
    // first write signal
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            frist_write_signal <= 1'b1 ;
        else if( prot_gen_state_c==PROT_GEN_IDLE )
            frist_write_signal <= 1'b1 ;
        else if( protocol_FIFO_wr_en_r )
            frist_write_signal <= 1'b0 ;
        else
            frist_write_signal <= frist_write_signal ;
    end
    // write FIFO
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            protocol_FIFO_wr_en_r   <= 1'b0 ;
            protocol_FIFO_wr_data_r <= { (1+1+IN_MOD_WIDTH+IN_DATA_WIDTH){1'b0} } ;
        end
        else if( (prot_gen_state_c == PROT_GEN_JUDGE) && (protocol_gen_eop) ) begin
                // The last transfer
            if(protocol_gen_val) begin // The last transfer is valid
                if( first_gen_signal ) begin // The first and also the last time
                    protocol_FIFO_wr_en_r   <= 1'b1 ;
                    protocol_FIFO_wr_data_r <= {1'b1, 1'b0, 4'b0, protocol_gen_data, 32'b0} ;
                end
                else begin // previous data is valid
                    protocol_FIFO_wr_en_r   <= 1'b1 ;
                    protocol_FIFO_wr_data_r <= {frist_write_signal, 1'b0, 4'b0, protocol_gen_data_ff, ACTION_ADDITION_SEQ} ;
                end
            end
            else begin // The last transfer is not valid
                protocol_FIFO_wr_en_r   <= 1'b1 ;
                protocol_FIFO_wr_data_r <= {frist_write_signal, 1'b1, 4'b0, protocol_gen_data_ff, 32'h12_34} ;
            end
        end
        else if( (prot_gen_state_c == PROT_GEN_LAST) ) begin
            if( protocol_gen_val_ff1 && (!first_gen_signal) ) begin
                    // The last transfert has not been executed
                protocol_FIFO_wr_en_r   <= 1'b1 ;
                protocol_FIFO_wr_data_r <= {frist_write_signal, 1'b1, 4'b0, protocol_gen_data_ff, 32'h12_34} ;
            end
            else begin
                    // else: send the 128-bit "0" to meet the shortest frame (32+42=76>64)
                protocol_FIFO_wr_en_r   <= 1'b1 ;
                protocol_FIFO_wr_data_r <= {1'b0, 1'b1, {(IN_MOD_WIDTH+IN_DATA_WIDTH-32){1'b0}}, 32'h12_34 } ;
            end
        end
        else if( (prot_gen_state_c == PROT_GEN_JUDGE) && (protocol_gen_val) ) begin
                // The middle transfer
            if( first_gen_signal ) begin // previous data is not valid
                protocol_FIFO_wr_en_r   <= 1'b0 ;
                protocol_FIFO_wr_data_r <= { (1+1+IN_MOD_WIDTH+IN_DATA_WIDTH){1'b0} } ;
            end
            else begin // previous data is 
                protocol_FIFO_wr_en_r   <= 1'b1 ;
                protocol_FIFO_wr_data_r <= {frist_write_signal, 1'b0, 4'b0, protocol_gen_data_ff, ACTION_ADDITION_SEQ} ;
            end
        end
        else begin
            protocol_FIFO_wr_en_r   <= 1'b0 ;
            protocol_FIFO_wr_data_r <= protocol_FIFO_wr_data_r ;
        end
    end

    // protocol generate finish
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            protocol_gen_finish_r <= 1'b0 ;
        else if( prot_gen_state_c==PROT_GEN_END )
            protocol_gen_finish_r <= 1'b1 ;
        else
            protocol_gen_finish_r <= 1'b0 ;
    end
    // frame info flow
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            DATALINK_dst_mac_fifo_o_r <= {(48){1'b0}} ;
            DATALINK_src_mac_fifo_o_r <= {(48){1'b0}} ;
            NETWORK_src_IP_fifo_o_r <= {(32){1'b0}} ;
            NETWORK_dst_IP_fifo_o_r <= {(32){1'b0}} ;
            TRANSPORT_src_port_fifo_o_r <= {(16){1'b0}} ;
            TRANSPORT_dst_port_fifo_o_r <= {(16){1'b0}} ;
        end
        else if( prot_gen_state_c==PROT_GEN_END ) begin
            DATALINK_dst_mac_fifo_o_r <= DATALINK_dst_mac_fifo_r ;
            DATALINK_src_mac_fifo_o_r <= DATALINK_src_mac_fifo_r ;
            NETWORK_src_IP_fifo_o_r <= NETWORK_src_IP_fifo_r ;
            NETWORK_dst_IP_fifo_o_r <= NETWORK_dst_IP_fifo_r ;
            TRANSPORT_src_port_fifo_o_r <= TRANSPORT_src_port_fifo_r ;
            TRANSPORT_dst_port_fifo_o_r <= TRANSPORT_dst_port_fifo_r ;
        end
        else begin
            DATALINK_dst_mac_fifo_o_r <= DATALINK_dst_mac_fifo_o_r ;
            DATALINK_src_mac_fifo_o_r <= DATALINK_src_mac_fifo_o_r ;
            NETWORK_src_IP_fifo_o_r <= NETWORK_src_IP_fifo_o_r ;
            NETWORK_dst_IP_fifo_o_r <= NETWORK_dst_IP_fifo_o_r ;
            TRANSPORT_src_port_fifo_o_r <= TRANSPORT_src_port_fifo_o_r ;
            TRANSPORT_dst_port_fifo_o_r <= TRANSPORT_dst_port_fifo_o_r ;
        end
    end
    // protocol length
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            protocol_length_r <= 11'b0 ;
        else if(prot_gen_state_c==PROT_GEN_IDLE)
            protocol_length_r <= 11'b0 ;
        else if(protocol_FIFO_wr_en)
            protocol_length_r <= protocol_length_r + 11'd16 ;
        else 
            protocol_length_r <= protocol_length_r ;
    end
    // calculate the CRC check sum
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            protocol_CRC_checksum_header <= 16'b0 ;
            protocol_CRC_checksum_r <= 16'b0 ;
        end
        else if( (prot_gen_state_c==PROT_GEN_IDLE) && (UDP_generator_rdy && protocol_gen_sav) ) begin
            protocol_CRC_checksum_header <= 16'b0 ;
            protocol_CRC_checksum_r <= 16'b0 ;
        end
        else if( protocol_FIFO_wr_en ) begin
            {protocol_CRC_checksum_header, protocol_CRC_checksum_r} <= 
                    protocol_CRC_checksum_header + protocol_CRC_checksum_r + 
                    protocol_FIFO_wr_data[127:112] + protocol_FIFO_wr_data[111:96] + protocol_FIFO_wr_data[95:80] +
                    protocol_FIFO_wr_data[79:64] + protocol_FIFO_wr_data[63:48] + protocol_FIFO_wr_data[47:32] +
                    protocol_FIFO_wr_data[31:16] + protocol_FIFO_wr_data[15:0] ;
        end
        else if( prot_gen_state_c == PROT_GEN_CRC_GEN )
            {protocol_CRC_checksum_header, protocol_CRC_checksum_r} <= protocol_CRC_checksum_header + protocol_CRC_checksum_r ;
        else begin
            protocol_CRC_checksum_header <= protocol_CRC_checksum_header ;
            protocol_CRC_checksum_r <= protocol_CRC_checksum_r ;
        end
    end



//**************************************************
// output assign
//**************************************************
    assign protocol_gen_rdy             = ((prot_gen_state_c==PROT_GEN_IDLE) || (prot_gen_state_c==PROT_GEN_JUDGE))?1'b1:1'b0 ;
    assign protocol_start_trans         = protocol_start_trans_r ;
    assign protocol_FIFO_wr_en          = protocol_FIFO_wr_en_r ;
    assign protocol_FIFO_wr_data        = protocol_FIFO_wr_data_r ;
    assign DATALINK_dst_mac_fifo_o      = DATALINK_dst_mac_fifo_o_r ;
    assign DATALINK_src_mac_fifo_o      = DATALINK_src_mac_fifo_o_r ;
    assign NETWORK_src_IP_fifo_o        = NETWORK_src_IP_fifo_o_r ;
    assign NETWORK_dst_IP_fifo_o        = NETWORK_dst_IP_fifo_o_r ;
    assign TRANSPORT_src_port_fifo_o    = TRANSPORT_src_port_fifo_o_r ;
    assign TRANSPORT_dst_port_fifo_o    = TRANSPORT_dst_port_fifo_o_r ;
    assign protocol_length              = protocol_length_r ;
    assign protocol_CRC_checksum        = protocol_CRC_checksum_r ;
    assign protocol_gen_finish          = protocol_gen_finish_r ;

endmodule
